-- Npc "fixes"
--
-- -- Add dialogs preset that will demonstrate how to use dialogs to future owner
-- -- Move busy chat to mobs_npc.get_attention,
   -- this also make the npc stop and look at the player if not busy
-- -- Add button to open npc inventory

--TODO: -- Set npc job on spawn among a list of presets
--TODO: -- Add button to open npc job settings ? Or use a special item, like a contract ?
--TODO: -- change npc texture (in the menu ? or using a special item, like a mirror ?)
--TODO: -- change npc job
--TODO: -- change npc action
--TODO: -- Set npc armor and attack values based on inventory content
--TODO: -- Bind npc to a chest to make them send their inventory to it (by giving them the key ?)


local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local S = minetest.get_translator(modname)

if not minetest.registered_entities['mobs_npc:npc'] then
  return
end
if not minetest.global_exists("simple_dialogs") then
  return
end

local jobs = mobs_npc.jobs or {}
local inventory_context = {}

dofile(modpath..'/dialogs/npc.lua')

local mcl = minetest.get_modpath("mcl_core") ~= nil

if not mobs_npc.drop_trade_item then
  -- The item used to trade with the npc
  --TODO: Should be setting with a default value
  --  An could be set per npc as it would allow to
  --  to register new mobs with other needs
  mobs_npc.drop_trade_item = (mcl and "mcl_raw_ores:raw_gold" or "default:gold_lump")
end
if not mobs_npc.config_item then
  -- The item used to configure the npc dialog and action
  --TODO: Should be setting with a default value
  --  An other mod could want to replace it with a more immersive craftitem...
  mobs_npc.config_item = (mcl and "mcl_core:stick" or "default:stick")
end

if not mobs_npc.get_npc_attention then
  mobs_npc.get_npc_attention = function(self, clicker)
    if self.state == "attack" or self.attack then
        mobs_npc.npc_talk(self, clicker, {"Grr!", "I'm kinda busy!"})
    elseif self.runaway then
        mobs_npc.npc_talk(self, clicker, {"Brr!", "I'm kinda busy!"})
    else
        -- make npc stop (but don"t prevent it from moving again)
        self.state = "stand"
        self.attack = nil
        self:set_animation("stand")
        self:set_velocity(0)
        -- -- order npc to stand
        local old_order = self.order
        self.order = "stand"
       -- revert to old order (maybe should be triggerd to formspec closing)
        minetest.after(30,function(ns,od) ns.order = od end, self, old_order)

        -- Look at the player
        mobs:yaw_to_pos(self, clicker:get_pos())
        return true
      end
    end
end

npc = minetest.registered_entities['mobs_npc:npc']

npc.on_rightclick = function(self, clicker)

        local item = clicker:get_wielded_item()
        local itemname = item:get_name()

        -- Tame (and heal)
        if mobs:feed_tame(self, clicker, 8, true, true) then return end

        --
        -- Heal with food groups items
        --TODO: There must be an easier way to know if items are food...
        --TODO: This whole block should have it's own function. mobs_npc.npc_feed ?
        local prop = self.object:get_properties()
        -- Starts by checking if full health
        if self.health < prop.hp_max then
          local name = clicker:get_player_name()
          -- food_bread = heals 5
          local food = {
              bread = 5,
              meat = 6,
              apple = 2,
              egg_fried = 2,
              mushroom = 1,
          }
          local itemdef = minetest.registered_items[itemname]
          if itemdef and itemdef.groups then
            for k,v in pairs(food) do
              if itemdef.groups['food_'..k] then
                -- increase health
                self.health = math.min(self.health + v, prop.hp_max)
                self.object:set_hp(self.health)
                -- if not in creative then take item
                if not mobs.is_creative(name) then
                  item:take_item()
                  clicker:set_wielded_item(item)
                end
              return
              -- break
              end
            end
          end
        end

        --
        -- Capture
        if mobs:capture_mob(self, clicker, nil, 5, 80, false, nil) then return end
        -- Don't continue if clicker is holding capture item but capture failed
        if itemname == "mobs:lasso" or itemname == "mobs:net" then return end
        --
        -- Protect
        if mobs:protect(self, clicker) then return end
        --
        -- Trade
        -- right clicking with gold lump drops random item from list
        if mobs_npc.drop_trade(self, clicker, mobs_npc.drop_trade_item, self.npc_drops or mobs_npc.npc_drops) then
          return
        end

        --
        -- Config
        -- owner can right-click with stick to show control formspec
        -- Use mobs_npc.config_item
        --TODO: This whole block should have it's own function. mobs_npc.npc_control_form ?
        local name = clicker:get_player_name()
        if item:get_name() == mobs_npc.config_item and (self.owner == name or
        minetest.check_player_privs(clicker, {protection_bypass = true}) )then
            local formspec = mobs_npc.get_controls_formspec(name, self)
            -- Add a button to open Npc inventory
            if mobs_npc.enable_inventory then
              inventory_context[name] = mobs_npc.set_npc_id(self)
              formspec = formspec
                  .. "item_image_button[3.375,1.30;0.8,0.8;default:chest;inventory;]"
                  .. "tooltip[inventory;"..S('Open inventory').."]"
            end
            minetest.show_formspec(name, "mobs_npc:controls", formspec)
            return
        end

        -- show simple dialog if enabled or idle chatter
        -- Why isn't mobs_npc.useDialogs a boolean ?
        --TODO: check if item is lasso or net
        --TODO: This whole block should have it's own function. mobs_npc.npc_dialog ?
        if mobs_npc.get_npc_attention(self, clicker) then
          if mobs_npc.useDialogs == "Y" and self.dialog then
            simple_dialogs.show_dialog_formspec(name, self)
          else
            mobs_npc.npc_talk(self, clicker, {
                S("Hello"), S("Hi there"), S("What a lovely day")})
          end
        end

      end



--
-- Save npc self into a protected block upon death.
--

--TODO: let player restore npc by clicking npc bones with a special item
local npc_bones = modname .. ":npc_bones"
minetest.register_node(npc_bones, {
  description = S("Npc Bones"),
  tiles = {"mobs_npc_workers_bones.png"},
  is_ground_content = false,
  groups = {not_in_creative_inventory = 1},
  drawtype = "nodebox",
  paramtype = "light",
  node_box = {
    type = "fixed",
    fixed = {
      {-0.3750, -0.5000, -0.3750, 0.3750, 0.2500, 0.3750}
    }
  },
  on_punch = function(pos, node, puncher, pointed_thing)

    -- Get node meta
    local meta = minetest.get_meta(pos)

    --TODO: Do we want to check if puncher is owner ? Maybe not
    --TODO: Do we want the puncher to hold a specific item ? Probably

    -- Get mob data
    local mobname = meta:get('mobname')
    if not mobname then return end -- sanity check
    local data = meta:get('data')

    -- Remove the old node
    minetest.set_node(pos, {name = 'air'})

    -- Re-spawn the npc
    pos.y = pos.y + 1
    local smob = minetest.add_entity(pos, mobname, data)

    -- Get npc self
    local ent = smob and smob:get_luaentity()
    if not ent then return end -- sanity check

    -- Mob was dead... set new health value
    local prop = ent.object:get_properties()
    local health = math.random(prop.hp_min or 15, prop.hp_max or 20)
    ent.health = health
    ent.old_health = health
  end
})


npc.on_die = function(self,pos)

  -- Don't bother for wild NPCs
  if not ( self.owner and self.owner ~= "" ) then return end

  -- Remove the cause of death
  if self.cause_of_death then
    -- local puncher = self.cause_of_death.puncher
    -- if puncher and type(puncher) == "userdata" then
      self.cause_of_death = nil
    -- end
  end

  -- Find ground
  local node
  pos.y = pos.y - 1
  for i = 1, 6 do
    node = minetest.get_node(pos)
    if node.name ~= 'air' then pos.y = pos.y + 1
    else break
    end
  end
  -- Set bones
  minetest.set_node(pos, {name = npc_bones})

  -- Set node meta
  local meta = minetest.get_meta(pos)
  meta:set_string("infotext", S("Npc's old bones (owned by @1)", self.owner) ..
                              S("\nPunch to respawn"))
  meta:set_string("owner", self.owner)
  meta:set_string("data", self:get_staticdata())
  meta:set_string("mobname", self.name)
end

npc.on_spawn = function(self)
  --TODO: look for preset jobs and pick one at random
  -- Look for preset dialogs and pick one at random
  if not self.dialog or not self.dialog.dlg then
    local list = mobs_npc.default_dialogs[self.name]
    if list then
      local n = math.random(1,#list)
      local dialogstr = table.concat(list[n], '\n')
      simple_dialogs.load_dialog_from_string(self,dialogstr)
    end
  end
  return true -- true to run only once
end
-- npc.stay_near = {
    -- {''}
    -- }

npc.follow = {
  'default:gold_ingot',
  'default:diamond'
  }



npc.do_custom = function(self, dtime)
  -- Don't bother is npc doesn't have a job... or their job doesn't exists
  if not ( self.job and jobs[self.job] and jobs[self.job].do_custom ) then
    return
  end

end

if minetest.get_modpath('thelowerroad') then

  local axisx = tonumber(minetest.settings:get("thelowerroad.axisx")) or 100
  local sinfactor = tonumber(minetest.settings:get("thelowerroad.sinfactor")) or 62
  local xmin = axisx - sinfactor
  local xmax = axisx + sinfactor

  local spawn_on ={  -- Main road materials
          'default:silver_sandstone_brick',
          'default:stonebrick',
          'default:desert_sandstone_brick',
          'default:desert_stonebrick',
              }
  local spawn_on_names = {}
  for _,name in ipairs(spawn_on) do
    spawn_on_names[name] = true
  end


  mobs:spawn({
      name = 'mobs_npc:npc',
      nodes = spawn_on,
      -- neighbors = {"group:grass"},
      min_light = 14,
      interval = 60,
      chance = 5000,
      min_height = 0,
      max_height = 50,
      day_toggle = true,
      active_object_count = 3,

      -- custom function to spawn only on the road
      on_spawn = function(self, pos)
        -- Unspawn if pos.x is not near the road
        if pos.x < xmin or pos.x > xmax then
          self.object:remove()
          return
        end
        local node
        for i = 1, 4 do
          -- Set stay near the node
          node = minetest.get_node(pos)
          if spawn_on_names[node.name] then
            self.stay_near = { {node.name}, 6 }
            break
          end
          pos.y = pos.y - 1
        end
        -- Should we abort if couldn't find the node
        -- if not spawn_on_names[node] then
          -- self.object:remove()
          -- return
        -- end
        --TODO set job ?
      end
    })
end

-- Hacky way of registering an API-like function
-- to ease creation of new mob definitions based on npc defaults
function mobs_npc.default_npc_def()

  local npc = table.copy(minetest.registered_entities['mobs_npc:npc'])
  return {
    type = npc.type,
    passive = npc.passive,
    damage = npc.damage,
    attack_type = npc.attack_type,
    attack_monsters = npc.attack_monsters,
    attack_npcs = npc.attack_npcs,
    owner_loyal = npc.owner_loyal,
    pathfinding = npc.pathfinding,
    hp_min = npc.hp_min,
    hp_max = npc.initial_properties.hp_max,     -- npc.initial_properties
    armor = npc.armor,
    collisionbox = npc.initial_properties.collisionbox,  -- npc.initial_properties
    visual = npc.initial_properties.visual,       -- npc.initial_properties
    mesh = npc.initial_properties.mesh,          -- npc.initial_properties
    drawtype = npc.drawtype,
    textures = npc.texture_list,    -- npc.initial_properties
    child_texture = npc.child_texture,
    makes_footstep_sound = npc.initial_properties.makes_footstep_sound, -- npc.initial_properties
    sounds = npc.sounds,
    walk_velocity = npc.walk_velocity,
    run_velocity = npc.run_velocity,
    jump = npc.jump,
    drops = npc.drops,
    water_damage = npc.water_damage,
    lava_damage = npc.lava_damage,
    light_damage = npc.light_damage,
    follow = npc.follow,
    view_range = npc.view_range,
    owner = npc.owner,
    order = npc.order,
    fear_height = npc.fear_height,
    animation = npc.animation,
    on_rightclick = npc.on_rightclick,
    on_spawn = npc.on_spawn,
    do_custom = npc.do_custom,
    on_die = npc.on_die,
  }

end

if mobs_npc.enable_inventory then
  minetest.register_on_player_receive_fields(function(player, formname, fields)
    local pname = player:get_player_name()
    if inventory_context and inventory_context[pname] then
      if formname == "mobs_npc:controls" then
        local npcId = inventory_context[pname] or nil
        local npcself = mobs_npc.get_npcself_from_id(npcId)
        if npcself ~= nil then
          if fields["inventory"] then
            mobs_npc.show_npc_inventory_formspec(npcself, pname)
          end
        end
      else inventory_context[pname] = nil
      end
    end
  end)
end
